home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / python-support / python-rdflib / rdflib / Literal.py < prev    next >
Encoding:
Python Source  |  2007-04-04  |  10.4 KB  |  351 lines

  1. from rdflib.Identifier import Identifier
  2. from rdflib.URIRef import URIRef
  3. from rdflib.Namespace import Namespace
  4. from rdflib.exceptions import Error
  5. from datetime import date,time,datetime
  6. from time import strptime
  7. import base64
  8.  
  9. try:
  10.     from hashlib import md5
  11. except ImportError:
  12.     from md5 import md5    
  13.  
  14. import logging
  15.  
  16. _logger = logging.getLogger(__name__)
  17.  
  18. class Literal(Identifier):
  19.     """
  20.     RDF Literal: http://www.w3.org/TR/rdf-concepts/#section-Graph-Literal
  21.  
  22.     >>> Literal(1).toPython()
  23.     1L
  24.     >>> cmp(Literal("adsf"), 1)
  25.     1
  26.     >>> lit2006 = Literal('2006-01-01',datatype=_XSD_NS.date)
  27.     >>> lit2006.toPython()
  28.     datetime.date(2006, 1, 1)
  29.     >>> lit2006 < Literal('2007-01-01',datatype=_XSD_NS.date)
  30.     True
  31.     >>> oneInt     = Literal(1)
  32.     >>> twoInt     = Literal(2)
  33.     >>> twoInt < oneInt
  34.     False
  35.     >>> Literal('1') < Literal(1)
  36.     False
  37.     >>> Literal('1') < Literal('1')
  38.     False
  39.     >>> Literal(1) < Literal('1')
  40.     True
  41.     >>> Literal(1) < Literal(2.0)
  42.     True
  43.     >>> Literal(1) < URIRef('foo')
  44.     True
  45.     >>> Literal(1) < 2.0
  46.     True
  47.     >>> Literal(1) < object  
  48.     True
  49.     >>> lit2006 < "2007"
  50.     True
  51.     >>> "2005" < lit2006
  52.     True
  53.     """
  54.  
  55.     __slots__ = ("language", "datatype", "_cmp_value")
  56.  
  57.     def __new__(cls, value, lang=None, datatype=None):
  58.         if datatype:
  59.             lang = None
  60.         else:
  61.             value,datatype = _castPythonToLiteral(value)
  62.             if datatype:
  63.                 lang = None
  64.         if datatype:
  65.             datatype = URIRef(datatype)
  66.         try:
  67.             inst = unicode.__new__(cls,value)
  68.         except UnicodeDecodeError:
  69.             inst = unicode.__new__(cls,value,'utf-8')
  70.         inst.language = lang
  71.         inst.datatype = datatype
  72.         inst._cmp_value = inst._toCompareValue()
  73.         return inst
  74.  
  75.     def __reduce__(self):
  76.         return (Literal, (unicode(self), self.language, self.datatype),)
  77.  
  78.     def __getstate__(self):
  79.         return (None, dict(language=self.language, datatype=self.datatype))
  80.  
  81.     def __setstate__(self, arg):
  82.         _, d = arg
  83.         self.language = d["language"]
  84.         self.datatype = d["datatype"]
  85.  
  86.     def __add__(self, val):
  87.         """
  88.         >>> Literal(1) + 1
  89.         2L
  90.         >>> Literal("1") + "1"
  91.         rdflib.Literal('11', language=None, datatype=None)
  92.         """
  93.  
  94.         py = self.toPython()
  95.         if isinstance(py, Literal):
  96.             s = super(Literal, self).__add__(val)            
  97.             return Literal(s, self.language, self.datatype)
  98.         else:
  99.             return py + val 
  100.  
  101.  
  102.     
  103.     def __lt__(self, other):
  104.         if other is None:
  105.             return False # Nothing is less than None
  106.         try:
  107.             return self._cmp_value < other
  108.         except TypeError, te:
  109.             return unicode(self._cmp_value) < other
  110.  
  111.     def __le__(self, other):
  112.         if other is None:
  113.             return False
  114.         if self==other:
  115.             return True
  116.         else:
  117.             return self < other
  118.  
  119.     def __gt__(self, other):
  120.         if other is None:
  121.             return True # Everything is greater than None
  122.         try:
  123.             return self._cmp_value > other
  124.         except TypeError, te:
  125.             return unicode(self._cmp_value) > other
  126.  
  127.     def __ge__(self, other):
  128.         if other is None:
  129.             return False
  130.         if self==other:
  131.             return True
  132.         else:
  133.             return self > other
  134.  
  135.     def __ne__(self, other):
  136.         """
  137.         Overriden to ensure property result for comparisons with None via !=.
  138.         Routes all other such != and <> comparisons to __eq__
  139.         
  140.         >>> Literal('') != None
  141.         True
  142.         >>> Literal('2') <> Literal('2')
  143.         False
  144.          
  145.         """
  146.         if other is None:
  147.             return True
  148.         else:
  149.             return not self.__eq__(other)
  150.  
  151.     def __eq__(self, other):
  152.         """        
  153.         >>> f = URIRef("foo")
  154.         >>> f is None or f == ''
  155.         False
  156.         >>> Literal("1", datatype=URIRef("foo")) == Literal("1", datatype=URIRef("foo"))
  157.         True
  158.         >>> Literal("1", datatype=URIRef("foo")) == Literal("2", datatype=URIRef("foo"))
  159.         False
  160.         >>> Literal("1", datatype=URIRef("foo")) == "asdf"
  161.         False
  162.         >>> oneInt     = Literal(1)
  163.         >>> oneNoDtype = Literal('1')
  164.         >>> oneInt == oneNoDtype
  165.         False
  166.         >>> Literal("1",_XSD_NS[u'string']) == Literal("1",_XSD_NS[u'string']) 
  167.         True
  168.         >>> Literal("one",lang="en") == Literal("one",lang="en")
  169.         True
  170.         >>> Literal("hast",lang='en') == Literal("hast",lang='de')
  171.         False
  172.         >>> oneInt == Literal(1)
  173.         True
  174.         >>> oneFloat   = Literal(1.0)
  175.         >>> oneInt == oneFloat
  176.         True
  177.         >>> oneInt == 1
  178.         True
  179.         """
  180.         if other is None:
  181.             return False
  182.         else:
  183.             return self._cmp_value==other
  184.  
  185.     def n3(self):
  186.         language = self.language
  187.         datatype = self.datatype
  188.         # unfortunately this doesn't work: a newline gets encoded as \\n, which is ok in sourcecode, but we want \n
  189.         #encoded = self.encode('unicode-escape').replace('\\', '\\\\').replace('"','\\"')
  190.         #encoded = self.replace.replace('\\', '\\\\').replace('"','\\"')
  191.  
  192.         # TODO: We could also chose quotes based on the quotes appearing in the string, i.e. '"' and "'" ...
  193.  
  194.         # which is nicer?
  195.         #if self.find("\"")!=-1 or self.find("'")!=-1 or self.find("\n")!=-1:
  196.         if self.find("\n")!=-1:
  197.             # Triple quote this string.
  198.             encoded=self.replace('\\', '\\\\')
  199.             if self.find('"""')!=-1: 
  200.                 # is this ok?
  201.                 encoded=encoded.replace('"""','\\"""')
  202.             if encoded.endswith('"'): encoded=encoded[:-1]+"\\\""
  203.             encoded='"""%s"""'%encoded
  204.         else: 
  205.             encoded='"%s"'%self.replace('\n','\\n').replace('\\', '\\\\').replace('"','\\"')
  206.         if language:
  207.             if datatype:    
  208.                 return '%s@%s^^<%s>' % (encoded, language, datatype)
  209.             else:
  210.                 return '%s@%s' % (encoded, language)
  211.         else:
  212.             if datatype:
  213.                 return '%s^^<%s>' % (encoded, datatype)
  214.             else:
  215.                 return '%s' % encoded
  216.  
  217.     def __str__(self):
  218.         return self.encode("unicode-escape")
  219.  
  220.     def __repr__(self):
  221.         return """rdflib.Literal('%s', language=%s, datatype=%s)""" % (str(self), repr(self.language), repr(self.datatype))
  222.  
  223.     def toPython(self):
  224.         """
  225.         Returns an appropriate python datatype derived from this RDF Literal
  226.         """
  227.         convFunc = _toPythonMapping.get(self.datatype, None)
  228.         
  229.         if convFunc:
  230.             rt = convFunc(self)
  231.         else:
  232.             rt = self
  233.         return rt
  234.  
  235.     def _toCompareValue(self):
  236.         try:
  237.             rt = self.toPython()
  238.         except Exception, e:
  239.             _logger.warning("could not convert %s to a Python datatype" % repr(self))
  240.             rt = self
  241.                 
  242.         if rt is self:
  243.             if self.language is None and self.datatype is None:
  244.                 return unicode(rt)
  245.             else:
  246.                 return (unicode(rt), rt.datatype, rt.language)
  247.         return rt
  248.  
  249.     def md5_term_hash(self):
  250.         d = md5(str(self))
  251.         d.update("L")
  252.         return d.hexdigest()
  253.  
  254.  
  255. _XSD_NS = Namespace(u'http://www.w3.org/2001/XMLSchema#')
  256.  
  257. #Casts a python datatype to a tuple of the lexical value and a datatype URI (or None)
  258. def _castPythonToLiteral(obj):
  259.     for pType,(castFunc,dType) in _PythonToXSD.items():
  260.         if isinstance(obj,pType):
  261.             if castFunc:
  262.                 return castFunc(obj),dType
  263.             elif dType:
  264.                 return obj,dType
  265.             else:
  266.                 return obj,None
  267.     return obj, None # TODO: is this right for the fall through case?
  268.  
  269. #Mappings from Python types to XSD datatypes and back (burrowed from sparta)
  270. _PythonToXSD = {
  271.     basestring : (None,None),
  272.     float      : (None,_XSD_NS[u'float']),
  273.     int        : (None,_XSD_NS[u'int']),
  274.     long       : (None,_XSD_NS[u'long']),
  275.     bool       : (None,_XSD_NS[u'boolean']),
  276.     date       : (lambda i:i.isoformat(),_XSD_NS[u'date']),
  277.     time       : (lambda i:i.isoformat(),_XSD_NS[u'time']),
  278.     datetime   : (lambda i:i.isoformat(),_XSD_NS[u'dateTime']),
  279. }
  280.  
  281. def _strToTime(v) :
  282.     return strptime(v,"%H:%M:%S")
  283.  
  284. def _strToDate(v) :
  285.     tstr = strptime(v,"%Y-%m-%d")
  286.     return date(tstr.tm_year,tstr.tm_mon,tstr.tm_mday)
  287.  
  288. def _strToDateTime(v) :
  289.     """
  290.     Attempt to cast to datetime, or just return the string (otherwise)
  291.     """
  292.     try:
  293.         tstr = strptime(v,"%Y-%m-%dT%H:%M:%S")
  294.     except:
  295.         try:
  296.             tstr = strptime(v,"%Y-%m-%dT%H:%M:%SZ")
  297.         except:
  298.             try:
  299.                 tstr = strptime(v,"%Y-%m-%dT%H:%M:%S%Z")
  300.             except:
  301.                 return v
  302.  
  303.     return datetime(tstr.tm_year,tstr.tm_mon,tstr.tm_mday,tstr.tm_hour,tstr.tm_min,tstr.tm_sec)
  304.  
  305. XSDToPython = {
  306.     _XSD_NS[u'time']               : _strToTime,
  307.     _XSD_NS[u'date']               : _strToDate,
  308.     _XSD_NS[u'dateTime']           : _strToDateTime,
  309.     _XSD_NS[u'string']             : None,
  310.     _XSD_NS[u'normalizedString']   : None,
  311.     _XSD_NS[u'token']              : None,
  312.     _XSD_NS[u'language']           : None,
  313.     _XSD_NS[u'boolean']            : lambda i:i.lower() in ['1','true'],
  314.     _XSD_NS[u'decimal']            : float,
  315.     _XSD_NS[u'integer']            : long,
  316.     _XSD_NS[u'nonPositiveInteger'] : int,
  317.     _XSD_NS[u'long']               : long,
  318.     _XSD_NS[u'nonNegativeInteger'] : int,
  319.     _XSD_NS[u'negativeInteger']    : int,
  320.     _XSD_NS[u'int']                : long,
  321.     _XSD_NS[u'unsignedLong']       : long,
  322.     _XSD_NS[u'positiveInteger']    : int,
  323.     _XSD_NS[u'short']              : int,
  324.     _XSD_NS[u'unsignedInt']        : long,
  325.     _XSD_NS[u'byte']               : int,
  326.     _XSD_NS[u'unsignedShort']      : int,
  327.     _XSD_NS[u'unsignedByte']       : int,
  328.     _XSD_NS[u'float']              : float,
  329.     _XSD_NS[u'double']             : float,
  330.     _XSD_NS[u'base64Binary']       : base64.decodestring,
  331.     _XSD_NS[u'anyURI']             : None,
  332. }
  333.  
  334. _toPythonMapping = {}
  335. _toPythonMapping.update(XSDToPython)
  336.  
  337. def bind(datatype, conversion_function):
  338.     """bind a datatype to a function for converting it into a Python instance."""
  339.     if datatype in _toPythonMapping:
  340.         _logger.warning("datatype '%s' was already bound. Rebinding." % datatype)
  341.     _toPythonMapping[datatype] = conversion_function
  342.  
  343.  
  344.  
  345. def test():
  346.     import doctest
  347.     doctest.testmod()
  348.  
  349. if __name__ == '__main__':
  350.     test()
  351.